/*
	Leola Programming Language
	Author: Tony Sparks
	See license.txt
*/
package leola.vm.lang.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

import leola.vm.Leola;
import leola.vm.lib.LeolaIgnore;
import leola.vm.lib.LeolaLibrary;


/**
 * The Input/Output Library
 * 
 * @author Tony
 *
 */
public class IOLeolaLibrary implements LeolaLibrary {

	/**
	 * The namespace of the IO Library.
	 */
	public static final String IO_NAMESPACE = "io";

	private Leola runtime;

	
	/* (non-Javadoc)
	 * @see leola.frontend.LeolaLibrary#init(leola.frontend.Leola)
	 */
	@LeolaIgnore
	public void init(Leola leola) throws Exception {		
		this.runtime = leola;
		this.runtime.putIntoNamespace(this, IO_NAMESPACE);
	}

	/**
	 * List the contents of the directory
	 * @param dir
	 * @return a list of all the files in the supplied directory
	 */
	public String[] list(String dir) {
		return new File(dir).list();
	}
	
	/**
	 * The current working directory
	 * @return the current working directory
	 */
	public static String pwd() {
		return System.getProperty("user.dir");
	}
	
	public void $includePath(String newPath) {		
		String[] paths = newPath.split(";");
		List<File> newIncludePath = new ArrayList<File>(paths.length);
		for(String path : paths) {
			newIncludePath.add(new File(path));
		}			
		
		this.runtime.setIncludePath(newIncludePath);
	}
	
	public static final boolean delete(String filename) throws Exception {
		File f = new File(filename);
		return ( f.delete() );
	}
	
	public static final void copy(String filename, String destination) throws Exception {
		copy(filename, destination, false);
	}
	
	public static final void xcopy(String filename, String destination) throws Exception {
		copy(filename, destination, true);
	}
	
	private static final void copy(String filename, String destination, boolean force) throws Exception {
		File src = new File(filename);
		if ( ! src.exists() ) {
			throw new IOException(filename + " doesn't exist!");
		}
		
		File dst = new File(destination);
		if ( dst.exists() ) {
			if ( force ) {
				if ( dst.delete() ) {
					throw new IOException("Unable to forcefully delete: " + destination);
				}
			}
			else {
				throw new IOException(destination + " already exists!");
			}
		}
		
		FileInputStream istream = new FileInputStream(src);
		FileOutputStream ostream = new FileOutputStream(dst);
		
		FileChannel dstChannel = ostream.getChannel();
		FileChannel srcChannel = istream.getChannel();
		try {
			long totalBytesTransferred = 0;
			long totalNeededForTransfer = srcChannel.size();
			while( totalBytesTransferred < totalNeededForTransfer ) {
				totalBytesTransferred += srcChannel.transferTo(totalBytesTransferred, totalNeededForTransfer-totalBytesTransferred, dstChannel);
			}
		}
		finally {
			try { srcChannel.close(); } catch(Exception e) {}
			try { dstChannel.close(); } catch(Exception e) {}
		}
	}
	
	public static final void move(String filename, String destination) throws Exception {
		move(filename, destination, false);
	}
	
	public static final void xmove(String filename, String destination) throws Exception {
		move(filename, destination, true);
	}
	
	private static final void move(String filename, String destination, boolean force) throws Exception {
		File src = new File(filename);
		if ( ! src.exists() ) {
			throw new IOException(filename + " doesn't exist!");
		}
		
		File dst = new File(destination);
		if ( dst.exists() ) {
			if ( force ) {
				if ( dst.delete() ) {
					throw new IOException("Unable to forcefully delete: " + destination);
				}
			}
			else {
				throw new IOException(destination + " already exists!");
			}
		}
		
		if ( ! src.renameTo(dst) ) {
			throw new IOException("Unable to move: " + filename + " to: " + destination );
		}
	}
	
	public static final Buffer newBuffer(int capacity) {
		return new Buffer(capacity);
	}

	public static final LeolaFile fopen(String filename, String mode) throws Exception {				
		RandomAccessFile raf = new RandomAccessFile(new File(filename), mode);
		return new LeolaFile(raf);
	}

	/**
	 * Reads in and stores into the buffer.
	 *
	 * @param file
	 * @param buffer
	 * @return
	 * @throws Exception
	 */
//	public static final int freadBuffer(LeolaFile file, Buffer buffer) throws Exception {
//		return file.readBuffer(buffer);
//	}
//	public static final int fread(LeolaFile file) throws Exception {
//		return file.read();
//	}
//	public static final boolean freadBoolean(LeolaFile file) throws Exception {
//		return file.readBoolean();
//	}
//	public static final byte freadByte(LeolaFile file) throws Exception {
//		return file.readByte();
//	}
//	public static final char freadChar(LeolaFile file) throws Exception {
//		return file.readChar();
//	}
//	public static final short freadShort(LeolaFile file) throws Exception {
//		return file.readShort();
//	}
//	public static final int freadInt(LeolaFile file) throws Exception {
//		return file.readInt();
//	}
//	public static final float freadFloat(LeolaFile file) throws Exception {
//		return file.readFloat();
//	}
//	public static final double freadDouble(LeolaFile file) throws Exception {
//		return file.readDouble();
//	}
//	public static final long freadLong(LeolaFile file) throws Exception {
//		return file.readLong();
//	}
//	/**
//	 * Reads a line.
//	 *
//	 * @param file
//	 * @return
//	 * @throws Exception
//	 */
//	public static final String freadString(LeolaFile file) throws Exception {
//	    return file.readString();
//	}
//
//	public static final String freadLine(LeolaFile file) throws Exception {
////	    return file.readUTF();
//		return file.readLine();
//	}
//	
//	/**
//	 * Reads all lines
//	 * @param file
//	 * @return an array containing all of the lines
//	 * @throws Exception
//	 */
//	public static final LeoArray freadLines(LeolaFile file) throws Exception {
//		return file.readLines();
//	}
//	
//	/**
//	 * Writes out bytes.
//	 *
//	 * @param file
//	 * @param buffer
//	 * @throws Exception
//	 */
//	public static final void fwriteBuffer(LeolaFile file, Buffer buffer) throws Exception {
//		file.writeBuffer(buffer);
//	}
//
//	public static final void fwrite(LeolaFile file, int b) throws Exception {
//		file.write(b);
//	}
//	public static final void fwriteBoolean(LeolaFile file, boolean b) throws Exception {
//		file.writeBoolean(b);
//	}
//	public static final void fwriteByte(LeolaFile file, byte b) throws Exception {
//		file.writeByte(b);
//	}
//	public static final void fwriteChar(LeolaFile file, char b) throws Exception {
//		file.writeChar(b);
//	}
//	public static final void fwriteShort(LeolaFile file, short b) throws Exception {
//		file.writeShort(b);
//	}
//	public static final void fwriteInt(LeolaFile file, int b) throws Exception {
//		file.writeInt(b);
//	}
//	public static final void fwriteFloat(LeolaFile file, float b) throws Exception {
//		file.writeFloat(b);
//	}
//	public static final void fwriteDouble(LeolaFile file, double b) throws Exception {
//		file.writeDouble(b);
//	}
//	public static final void fwriteLong(LeolaFile file, long b) throws Exception {
//		file.writeLong(b);
//	}
//	
//	/**
//	 * Writes out a line
//	 *
//	 * @param file
//	 * @param line
//	 * @throws Exception
//	 */
//	public static final void fwriteString(LeolaFile file, String line) throws Exception {
//		file.writeString(line);
//	}
//	
//	public static final void fwriteLine(LeolaFile file, String line) throws Exception {
//		file.writeLine(line);
//	}
//
//	public static final void fwriteLines(LeolaFile file, LeoArray array) throws Exception {
//		file.writeLines(array);
//	}
//	
//	/**
//	 * Seeks into a file.
//	 *
//	 * @param pFile
//	 * @param pos
//	 * @throws Exception
//	 */
//	public static final void fseek(LeolaFile file, long pos) throws Exception {
//		file.seek(pos);
//	}
//
//	/**
//	 * Closes the file.
//	 *
//	 * @param file
//	 * @throws Exception
//	 */
//	public static final void fclose(LeolaFile file) throws Exception {
//		file.close();
//	}
//
//	/**
//	 * Returns the length of the file in bytes
//	 * @param file
//	 * @return
//	 * @throws Exception
//	 */
//	public static final long flength(LeolaFile file) throws Exception {
//		return file.length();
//	}
//
//	/**
//	 * Sets the new length of the file.
//	 *
//	 * @param file
//	 * @param newLength
//	 * @throws Exception
//	 */
//	public static final void fsetLength(LeolaFile file, int newLength) throws Exception {
//		file.setLength(newLength);
//	}
}

